package com.urban.pooling.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.urban.pooling.domain.GeoPoint;
import com.urban.pooling.domain.Route;
import com.urban.pooling.domain.RouteRequest;
import com.urban.pooling.domain.RouteRequestStatus;
import com.urban.pooling.repository.GeoPointRepository;
import com.urban.pooling.repository.RouteRepository;
import com.urban.pooling.repository.RouteRequestRepository;
import com.urban.pooling.repository.RouteRequestStatusRepository;

@Service
// @ComponentScan("com.urban.pooling.dao")
public class RouteService {
	private static final Logger LOG = LoggerFactory.getLogger(RouteService.class);

	@Autowired
	private RouteRepository routeRepo;

	@Autowired
	private RouteRequestRepository routeRequestRepo;

	@Autowired
	private GeoPointRepository geoPointRepo;

	@Autowired
	private GoogleMapsService googleMapsService;

	@Autowired
	private RouteRequestStatusRepository routeRequestStatusRepo;

	private Executor threadPool;

	public RouteService() {
		threadPool = Executors.newCachedThreadPool();
	}

	@Transactional
	public void add(Route route) {
		LOG.debug("Adding route:" + route);
		route.setLastModified(new Date());
		route = routeRepo.save(route);

		// TODO : check if there are any problems with synchronization
		threadPool.execute(new WaypointFinder(geoPointRepo, route));

		// TODO : do this in a worker thread
		/*
		 * List<GeoPoint> waypoints = getWaypoints(route);
		 * geoPointRepo.delete(route.getStartPoint());
		 * geoPointRepo.delete(route.getDestPoint());
		 * geoPointRepo.save(waypoints);
		 */

	}

	@Transactional
	public List<Route> findAll() {
		return Lists.newArrayList(routeRepo.findByStartTimeGreaterThanOrderByLastModifiedDesc(new Date()));
	}

	public List<Route> search(GeoPoint startPoint, GeoPoint destPoint, Date lowerStartDate, Date upperStartDate,
			int availableSeats, float radius) {
		if (startPoint == null || destPoint == null || lowerStartDate == null || upperStartDate == null
				|| availableSeats < 1 || radius < 0) {
			throw new IllegalArgumentException("Null argument");
		}
		return Lists.newArrayList(routeRepo.searchRoutes(startPoint.getLat(), startPoint.getLng(), destPoint.getLat(),
				destPoint.getLng(), lowerStartDate, upperStartDate, availableSeats, radius));
	}

	public Route findOne(Integer routeId) {
		Route route = routeRepo.findOne(routeId);
		route.setOccupiedSeats(Integer.parseInt(routeRepo.getOccupiedSeats(route.getRouteId()).toString()));
		route.setWaypoints(getPassengerWaypoints(route));
		return route;
	}

	public void deleteRoute(Integer id) {
		routeRepo.delete(id);
	}

	/**
	 * Return a list containing the pickup and dropoff points of all accepted
	 * route requests.
	 * 
	 * @param route
	 * @return
	 */
	public List<GeoPoint> getPassengerWaypoints(Route route) {
		List<GeoPoint> waypoints = new ArrayList<GeoPoint>();
		RouteRequestStatus acceptedStatus = routeRequestStatusRepo.findByStatus(RouteRequestStatus.ACCEPTED);
		Iterable<RouteRequest> routeRequests = routeRequestRepo.findByRouteAndRouteRequestStatus(route, acceptedStatus);
		for (RouteRequest routeRequest : routeRequests) {
			waypoints.add(routeRequest.getPickupPoint());
			waypoints.add(routeRequest.getDestinationPoint());
		}
		return waypoints;
	}

	public Iterable<Route> findByUsername(String username) {
		return routeRepo.findByUsername(username);
	}
}
